#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/params.h"
#include "include/libmod.h"
#include "include/modid.h"
#include "include/vmerr.h"
#include "include/nr_excp.h"
#include "mod_bv32vm.h"

struct bv32vm_ctx {
    struct {
        uint32 matp;
    }csr;
    int8 * memory;
    uint64 memsz;
    bool vm_enable;
    bool mod_enable;
};

status bv32vm_init (struct environ * env, struct module * mod);
status bv32vm_start (struct environ * env, struct module * mod); 
status bv32vm_stop (struct environ * env, struct module * mod); 
status bv32vm_pause (struct environ * env, struct module * mod); 
status bv32vm_resume (struct environ * env, struct module * mod); 
status bv32vm_ctrl (struct environ * env, struct module * mod, uint64 arg);
status bv32vm_phyrw (struct mem_phyrw_req req);
status bv32vm_virtrw (struct mem_virtrw_req req);
status bv32vm_csrrw (struct cpu_csrrw_req req);
status bv32_convert (int8 * memory, uint32 va, uint32 pgt_addr, uint32* pa, uint32* flags);
int phyaddr_ok(memaddr_t paddr, uint64 memsz, enum rwmod rw); // 检查物理地址是否合法
void memory_phyrw (int8* memory, memaddr_t paddr, word send, word* recv, enum rwmod rw); // 物理访存

static void
csr_apply (struct rv32cpu* cpu, struct bv32vm_ctx * this) {
    (void) cpu;
    if (this->csr.matp) {
        this->vm_enable = true;
    } else {
        this->vm_enable = false;
    }
}

status
bv32vm_assemble (struct module * mod) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * ctx = (struct bv32vm_ctx*)malloc (sizeof (struct bv32vm_ctx));
    bzero (ctx,sizeof (struct bv32vm_ctx));
    if (ctx == NULL) {
        stat |= ERR_NOMEM;
        goto error;
    }
    mod->context = ctx;
    mod->init = bv32vm_init;
    mod->start = bv32vm_start;
    mod->stop = bv32vm_stop;
    mod->pause = bv32vm_pause;
    mod->resume = bv32vm_resume;
    mod->modctl = bv32vm_ctrl;
    mod->stat = MOD_STAT_UNINITIALIZED;
    mod->modid = BV32VM_ID;
    strncpy (mod->name, "Biscuit-VirtMem", MAX_MOD_NAMESZ);
    stat |= module_add_item (mod, MI_MEM_PHYRW, bv32vm_phyrw);
    stat |= module_add_item (mod, MI_MEM_VIRTRW, bv32vm_virtrw);
    stat |= module_add_item (mod, MI_CPU_CSRRW, bv32vm_csrrw);
    if (stat == STAT_SUCCESS)
        mod->magic = BISCUIT_MAGIC;
    return stat;
    error:
    return stat;
}

status
bv32vm_init (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    if (mod->stat != MOD_STAT_UNINITIALIZED) {
        stat |= ERR_STATUS;
        goto error;
    }
    struct bv32vm_ctx * ctx = (struct bv32vm_ctx *) mod->context;
    ctx->memory = env->mem.data;
    ctx->memsz = env->mem.size;
    ctx->csr.matp = 0;
    ctx->mod_enable = false;
    ctx->vm_enable = false;
    mod->stat = MOD_STAT_STOPPED;
    return stat;
    error:
    return stat;
}

status
bv32vm_start (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) mod->context;
    if (mod->stat != MOD_STAT_STOPPED) {
        stat |= ERR_STATUS;
        goto error;
    }
    mod->stat = MOD_STAT_RUNNING;
    this->csr.matp = 0;
    this->mod_enable = true;
    this->vm_enable = false;
    this->memory = env->mem.data;
    this->memsz = env->mem.size;
    return stat;
    error:
    return stat;
}

status
bv32vm_stop (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) mod->context;
    (void) env;
    if (mod->stat != MOD_STAT_RUNNING) {
        stat |= ERR_STATUS;
        goto error;
    }
    bzero (this, sizeof (struct bv32vm_ctx));
    mod->stat = MOD_STAT_STOPPED;
    return stat;
    error:
    return stat;
}

status
bv32vm_pause (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) mod->context;
    (void) env;
    if (mod->stat != MOD_STAT_RUNNING) {
        stat |= ERR_STATUS;
        goto error;
    }
    this->vm_enable = false;
    mod->stat = MOD_STAT_PAUSED;
    return stat;
    error:
    return stat;
}

status
bv32vm_resume (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) mod->context;
    if (mod->stat != MOD_STAT_PAUSED) {
        stat |= ERR_STATUS;
        goto error;
    }
    csr_apply (&env->cpu,this); // 根据csr决定是否开启分页
    mod->stat = MOD_STAT_RUNNING;
    return stat;
    error:
    return stat;
}

status
bv32vm_ctrl (struct environ * env, struct module * mod, uint64 arg) {
    // 暂时没啥别的需求了
    (void) env;
    (void) mod;
    (void) arg;
    return STAT_SUCCESS;
}

/* bv32 分页方案 
 * L1PT = PGTADDR
 * VA:    | VPN.0 [31:22] | VPN.1 [21:12] |   OFFSET [11:0]  |
 * PTE:   | PPN.0 [31:22] | PPN.1 [21:12] |   FLAGS  [11:0]  |
 * L2PT = |  L1PT[VA.VPN.0].PPN [31:12]   |   000000000000   |
 * PA  =  |  L2PT[VA.VPN.1].PPN [31:12]   | VA.OFFSET [11:0] |
 * 二级页表,每个页表4K,页表项4B. 每个页表有1024个页表项.
 */

// status
// bv32_convert (int8 * memory, uint32 va, uint32 pgt_addr, uint32* pa, uint32* flags) {
//     status stat = STAT_SUCCESS;
//     uint32 * l1_pte_list, * l2_pte_list;
//     typedef uint32 pte_t;
//     uint32 pte;
//     uint32 phy_addr;
//     if (pgt_addr + (~PG4K_MASK) >= MAX_MEMSZ) {
//         stat |= ERR_MADDR;
//         goto error;
//     }
//     if ((pgt_addr & (~PG4K_MASK)) != 0) {
//         stat |= ERR_MADDR;
//         goto error;
//     }
//     l1_pte_list = (uint32*)(memory + pgt_addr);
//     pte = l1_pte_list[L1_PTE_INDEX(va)];
//     if (!(pte & PTE_V)) {
//         stat |= ERR_MFLAG; //MISSING V
//         goto error;
//     }
//     l2_pte_list = (uint32*)(memory + (pte & PG4K_MASK));
//     pte = l2_pte_list[L2_PTE_INDEX(va)];
//     if (!(pte & PTE_V)) {
//         stat |= ERR_MFLAG; // MISSING V
//         goto error;
//     }
//     phy_addr = ((pte&PG4K_MASK) | (PADDR_OFFSET(va)));
//     *pa = phy_addr;
//     *flags = pte & FLAG_MASK;
//     return stat;
//     error:
//     return stat;
// }

status
bv32_convert (int8 * memory, uint32 va, uint32 pgt_addr, uint32* pa, uint32* flags) {
    status stat = STAT_SUCCESS;
    typedef uint32 pte_t;
    pte_t * pte;
    uint32 pbase;
    if (pgt_addr > MAX_MEMSZ) {
        stat |= ERR_MADDR;
        goto error;
    }
    pte = (pte_t*)(memory+pgt_addr); // L1PTE
    pte += L1_PTE_INDEX(va);
    if (!(*pte & PTE_V)) {
        stat |= ERR_MFLAG;
        goto error;
    }
    pte = (pte_t*)(memory+(*pte & ~PG4K_MASK));
    pte += L2_PTE_INDEX(va);
    if (!(*pte & PTE_V)) {
        stat |= ERR_MFLAG;
        goto error;
    }
    pbase = (*pte & ~PG4K_MASK);
    *pa = pbase + (va&PG4K_MASK);
    *flags = (*pte & FLAG_MASK);
    return stat;
    error:
    return stat;
}

int
phyaddr_ok(memaddr_t paddr, uint64 memsz, enum rwmod rw) {
    if (IS_32BIT(rw) && (paddr+4 > memsz || paddr&0x1))
        goto bad;
    if (IS_16BIT(rw) && (paddr+2 > memsz || paddr&0x1))
        goto bad;
    if (IS_8BIT(rw) && paddr+1 > memsz)
        goto bad;
    return 1;
    bad:
    return 0;
} 

void
memory_phyrw (int8* memory, memaddr_t paddr, word send, word* recv, enum rwmod rw) {
    if (paddr >= MAX_MEMSZ) return;
    if (IS_32BIT(rw)) {
        if (IS_RMODE(rw)){ 
            *(recv) = ((int32*)memory)[paddr>>2];
        }
        if (IS_WMODE(rw)) {
            ((int32*)memory)[paddr>>2] = send;
        }
    }
    if (IS_16BIT(rw)) {
        if (IS_RMODE(rw)) 
            *(recv) = ((int16*)memory)[paddr>>1];
        if (IS_WMODE(rw))
            ((int16*)memory)[paddr>>1] = send;
    }
    if (IS_8BIT(rw)) {
        if (IS_RMODE(rw)) 
            *(recv) = ((int8*)memory)[paddr];
        if (IS_WMODE(rw))
            ((int8*)memory)[paddr] = send;
    }
}



status
bv32vm_phyrw (struct mem_phyrw_req req) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) req.ctx;
    if (!phyaddr_ok(req.addr, this->memsz, req.rw)) {
        stat |= ERR_MADDR;
        raise_excep_sync (req.env, EXCP_ACCESS_INVADDR);
        goto error;
    }
    // if (req.addr == 0x3000) {
    //     printf ("warning:writing to 0x3000 PC:0x%X\n", req.env->cpu.pc);
    //     exit (0);
    // }
    memory_phyrw (this->memory, req.addr, req.send, req.recv, req.rw);
    req.rstat->req_ack = true;
    return stat;
    error:
    return stat;
}

status
bv32vm_virtrw (struct mem_virtrw_req req) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) req.ctx;
    uint32 flags;
    uint32 paddr;
    if (!this->vm_enable) {
        paddr = req.addr; // 未启用vm时,使用物理地址
        goto phyrw;
    }
    stat = bv32_convert (this->memory, req.addr, this->csr.matp, &paddr, &flags);
    // if (stat) {
    //     printf ("virtmem: vaddr:0x%X,paddr:0x%X\n", req.addr, paddr);
    // }
    if (stat) {
        if (IS_XMODE(req.rw)) {
            raise_excep_sync (req.env, EXCP_INST_PGFT);
        } else if (IS_RMODE(req.rw)){
            raise_excep_sync (req.env, EXCP_LOAD_PGFT);
        } else if (IS_WMODE(req.rw)){
            raise_excep_sync (req.env, EXCP_STOR_PGFT);
        }
        goto finish;
    }
    if (IS_RMODE (req.rw) && !(flags & PTE_R)){
        stat |= ERR_MFLAG; // MISSING R
        raise_excep_sync (req.env, EXCP_ACCESS_MIS_R);
        goto finish;
    }
    if (IS_XMODE(req.rw) && !(flags & PTE_X)) {
        stat |= ERR_MFLAG; // MISSING X
        raise_excep_sync (req.env, EXCP_ACCESS_MIS_X);
        goto finish;
    }
    if (IS_WMODE(req.rw) && !(flags & PTE_W)) {
        stat |= ERR_MFLAG; // MISSING W
        raise_excep_sync (req.env, EXCP_ACCESS_MIS_W);
        goto finish;
    }
    if ((req.env->cpu.mode == CPU_MODE_U) && (!(flags & PTE_U))) {
        stat |= ERR_MFLAG; // MISSING U
        raise_excep_sync (req.env, EXCP_ACCESS_MIS_U);
        goto finish;
    }
    phyrw:
    if (!phyaddr_ok(paddr, this->memsz, req.rw)) {
        stat |= ERR_MADDR;
        // NOTE:暂时在这里抛访存错误
        raise_excep_sync (req.env, EXCP_ACCESS_INVADDR); 
        goto finish;
    }
    memory_phyrw (this->memory, paddr, req.send, req.recv, req.rw);
    finish:
    req.rstat->req_ack = true;
    return stat;
}


status
bv32vm_csrrw (struct cpu_csrrw_req req) {
    status stat = STAT_SUCCESS;
    struct bv32vm_ctx * this = (struct bv32vm_ctx *) req.ctx;
    if (!this->mod_enable) goto nop;
    if (req.addr == CSR_MATP) { 
        if (IS_RMODE(req.rw)) {
            *(req.recv) = this->csr.matp;
        }
        if (IS_WMODE(req.rw)) {
            this->csr.matp = req.send;
            //printf ("bv32vm: PGTB:0x%X installed\n", this->csr.matp);
            csr_apply (&req.env->cpu, this);
            // printf ("vm: paging enable");
            // fflush (stdout);
        }
        req.rstat->req_ack = true;
    }
    nop:
    return stat;
}